#include "lm3s2965.h"
#include "hw_ints.h"
#include "hw_memmap.h"
#include "hw_types.h"
#include "debug.h"
#include "gpio.h"
#include "MagicSmokeMachine.h"
#include "interrupt.h"
#include "sysctl.h"
#include "uart.h"
#include "timer.h"
#include "systick.h"
#include "URF.h"
#include <stdlib.h>
#include <stdio.h>


#define	URF_TIMEOUT 5*TICKS_PER_SEC // Time in seconds before timeout

#define URF_BASE UART0_BASE
#define ENABLE_BASE GPIO_PORTA_BASE
#define ENABLE_PIN GPIO_PIN_3
#define ENABLE_PERIPH SYSCTL_PERIPH_GPIOA

volatile char validRead = false;	// is the range reading valid, used after switching sensors
char range[3];						// range value to return
volatile char ReadingRange = false;
volatile int counter = 0;
tBoolean timeoutOccured = false;


// setup ultrasonic range sensor 
void initURF(void)
{	
	// enable peripherals
	SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

	// Configure the GPIO pin muxing for the UART function.
	GPIOPinConfigure(GPIO_PA0_U0RX);
	GPIOPinConfigure(GPIO_PA1_U0TX);

	// Set GPIO A0 and A1 as UART pins.
	GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

	// configure UART protocol
	UARTConfigSetExpClk(URF_BASE, 7812500, 9600,UART_CONFIG_WLEN_8|UART_CONFIG_STOP_ONE |UART_CONFIG_PAR_NONE);	// 9600 baud, 8 data bits, no parity, one stop bit

	// disable FIFO
	UARTFIFODisable(URF_BASE);

	// enable interrupts
	IntEnable(INT_UART0);
	UARTIntEnable(URF_BASE, UART_INT_RX);    // | UART_INT_RT);


	/*************** set ENABLE pins ******************/
	SysCtlPeripheralEnable(ENABLE_PERIPH);
	GPIODirModeSet(ENABLE_BASE, ENABLE_PIN, GPIO_DIR_MODE_OUT);
	GPIOPadConfigSet(ENABLE_BASE, ENABLE_PIN, GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD);
	/*************************************************/

	/*************** setup timeout timer *************/
	SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER2);
	TimerConfigure(TIMER2_BASE,TIMER_CFG_32_BIT_OS);
	TimerIntEnable(TIMER2_BASE,TIMER_TIMA_TIMEOUT);
	IntEnable(INT_TIMER2A);
	/************************************************/
	// enable processor interrupts
	IntMasterEnable();

	// enable UART
	UARTEnable(URF_BASE);
}


void displayURFRange(){
    printf("URF = %s cm\t", getURFRangeString());
}

range_t getURFRange()
{
	return atoi(getURFRangeString());
}

char* getURFRangeString()
{
	enableURF();

	// start timer
	TimerLoadSet(TIMER2_BASE,TIMER_A, URF_TIMEOUT);
	TimerEnable(TIMER2_BASE,TIMER_A);

	// wait until we've gotten a valid read
	while(!validRead){
		// wait
		if(timeoutOccured)
		{
			puts("No URF range detected");
			timeoutOccured = false;
			TimerDisable(TIMER2_BASE,TIMER_A);
			return "-1";
		}
	}
	disableURF();
	return range;
}

void UART0_Int_Handler(void){
    unsigned long ulStatus;
	ulStatus = UARTIntStatus(UART0_BASE, true);

	    //
	    // Clear the asserted interrupts.
	    //
	    UARTIntClear(UART0_BASE, ulStatus);

	while(UARTCharsAvail(UART0_BASE))
    {

        if(ReadingRange){
        	range[counter] = UARTCharGetNonBlocking(UART0_BASE);
        	counter ++;
        	if (counter == 3){
        		counter = 0;
        		ReadingRange = false;
        		validRead = true;
        	}
        }else{
        	char value = UARTCharGetNonBlocking(UART0_BASE);
        	if(value =='R'){
        		ReadingRange = true;
        	}
        }

    }

}


void enableURF()
{
	// reinitialize values
	validRead = false;
	ReadingRange = false;
	counter = 0;

	// ACTIVE HIGH
	GPIOPinWrite(ENABLE_BASE, ENABLE_PIN, 0xFF);
	UARTIntClear(UART0_BASE, 0xFF);	// Clear any interrupts
	if (UARTCharsAvail(UART0_BASE)){
		UARTCharGetNonBlocking(UART0_BASE); // do a read to clear uart register
	}

	UARTIntEnable(URF_BASE, UART_INT_RX);	// reenable interrupts
}

void disableURF()
{
	// Pull enable line low
	GPIOPinWrite(ENABLE_BASE, ENABLE_PIN, 0x00);

	// disable UART0 interrupts
	UARTIntDisable(URF_BASE, UART_INT_RX);

}



void URF_TIMER_Int_Handler(void)
{
	TimerIntClear(TIMER2_BASE, TIMER_TIMA_TIMEOUT);
	timeoutOccured = true;
}
